<?php

class Image {

    const FORMAT_JPEG = 'jpeg';
    const FORMAT_PNG = 'png';
    const FORMAT_GIF = 'gif';

    private $image;
    private $path;
    private $type;

    private function __construct($image,$path,$type) {
        $this->image = $image;
        $this->path = $path;
        $this->type = $type;
    }

    private static function normalizeType($type) {
        $type = strtolower($type);
        if ($type === 'pjpeg') {
            $type = self::FORMAT_JPEG;
        }
        return $type;
    }

    /**
     * @return Image
     */
    public static function load($path, $type = null) {

        File::checkExistsAndReadable($path);

        if ($type === null) {

            $hInfo = getimagesize($path);
            if (!$hInfo) self::checkSupportedType('unknown type');

            $oMime = new MimeType($hInfo['mime']);
            self::checkSupportedMimeType($oMime);

            $type = $oMime->getSubtype();

        } else {
            $type = self::normalizeType($type);
            self::checkSupportedType($type);
        }

        $func = 'imagecreatefrom' . $type;

        if (!$image = @$func($path))
            throw new Exception("cannot create image: $func($path)");

        return new self($image, realpath($path), $type);
    }

    public function getWidth()  { return ImageSX($this->image); }
    public function getHeigth() { return ImageSY($this->image); }
    public function getFormat() { return $this->type; }

    private function getWidthToHeightRelation() {
        return $this->getWidth() / $this->getHeigth();
    }

    public function resizeHard($newWidth, $newHeight) {
        $this->doResize($newWidth, $newHeight);
    }

    public function resizeIscribe($maxWidth, $maxHeight = null, $enlargeIfNeeds = false) {

        if ($maxHeight === null) $maxHeight = $maxWidth;

        if (($maxWidth / $maxHeight) < $this->getWidthToHeightRelation()) {
            $this->resizeIscribeWidthOnly($maxWidth, $enlargeIfNeeds);
        } else {
            $this->resizeIscribeHeightOnly($maxHeight, $enlargeIfNeeds);
        }
    }

    public function resizeIscribeWidthOnly($newWidth, $enlargeIfNeeds = false) {
        $this->doResize($newWidth, $newWidth / $this->getWidthToHeightRelation(), $enlargeIfNeeds);
    }

    public function resizeIscribeHeightOnly($newHeight, $enlargeIfNeeds = false) {
        $this->doResize($this->getWidthToHeightRelation() * $newHeight, $newHeight, $enlargeIfNeeds);
    }

    private function doResize($newWidth, $newHeight, $enlargeIfNeeds) {

        if (!$enlargeIfNeeds &&
            ($newWidth  >= $this->getWidth()) &&
            ($newHeight >= $this->getHeigth())
        ) {
            return;
        }

        $newImage = imagecreatetruecolor($newWidth, $newHeight);

        imagecopyresampled(
            $newImage, $this->image,
            0, 0, 0, 0,
            $newWidth, $newHeight, $this->getWidth(), $this->getHeigth()
        );

        $this->image = $newImage;
    }

    public function save() {
        $this->doSave($this->path, $this->type);
    }

    public function saveAs($path, $type = null) {

        if (!$type) {
           $type = $this->type;
        } else {
            self::checkSupportedType($type);
        }

        $this->doSave($path, $type);
    }

    private function doSave($path, $type) {

        $func = 'image' . $type;

        if (!@$func($this->image, $path))
            throw new Exception("cant save image: $func($path)");

        $this->path = $path;
        $this->type = $type;
    }

    public function getPath() {
        return $this->path;
    }

    public static function createImgTag($path, $add = '')   {

        $url = File::getUrl(realpath($path));
        list($w, $h) = getimagesize($path);

        return '<img src="'.$url.'" width="'.$w.'" height="'.$h.'" '.$add.' />';
    }

    private static function getTypeFromExtension($path) {

        $ext = preg_match('/\.([^.]*)$/',$path,$m) ? $m[1] : '';
        $type = strtolower($ext);
        if ($type == 'jpg') $type = self::FORMAT_JPEG;

        return $type;
    }

    public static function isSupportedMimeType(MimeType $oMime) {
        if (!$oMime->isImage()) return false;
        return self::isSupportedType( self::normalizeType($oMime->getSubtype()) );
    }

    private static function isSupportedType($type) {

        $types = array(
            self::FORMAT_JPEG,
            self::FORMAT_GIF,
            self::FORMAT_PNG,
        );

        return in_array($type, $types) ? $type : false;
    }

    private static function checkSupportedType($type) {
        if (!self::isSupportedType($type))
            throw new Exception("Unsupported image type [$type]");
    }

    private static function checkSupportedMimeType(MimeType $oMime) {
        if (!self::isSupportedMimeType($oMime))
            throw new Exception("Unsupported mime image type [" . $oMime->toString() . "]");
    }

    public static function isSupportedFile($path) {

        $hInfo = getimagesize($path);
        if (!$hInfo) return false;

        return self::isSupportedMimeType( new MimeType($hInfo['mime']) );
    }

}

